/*
 *  This file is part of LifeBudget - Copyright 2010 Clifton Eaton
 *  ------------------------------------------------------------------
 *  LifeBudget is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  LifeBudget is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with LifeBudget.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.lifebudget.app.client.mvc.view.paycheck;

import java.util.List;

import org.lifebudget.app.client.events.AppEvents;
import org.lifebudget.app.client.mvc.model.CategoryAmount;
import org.lifebudget.app.client.mvc.model.PaycheckCalculator;
import org.lifebudget.app.client.mvc.model.PaycheckCalculator.Interval;
import org.lifebudget.app.client.mvc.view.AppView;
import org.lifebudget.app.client.mvc.view.TabView;

import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.Style.LayoutRegion;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MessageBoxEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.Dispatcher;
import com.extjs.gxt.ui.client.mvc.View;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
import com.extjs.gxt.ui.client.store.TreeStore;
import com.extjs.gxt.ui.client.util.IconHelper;
import com.extjs.gxt.ui.client.util.Margins;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.TabItem;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.FormPanel;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign;
import com.extjs.gxt.ui.client.widget.layout.BorderLayout;
import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.extjs.gxt.ui.client.widget.treepanel.TreePanel;
import com.google.gwt.core.client.GWT;

/**
 * The Class PaycheckCalculatorView sets up the environment for
 * managing PaycheckCalculators.  The view contains a list of Paycheck
 * Calculators, and mechanisms for adding, removing, and selecting them.  It 
 * also sets up containers for the summary and editing views.
 */
public class PaycheckCalculatorView extends View {

	
	/** The center. */
	private LayoutContainer center;
	
	/** The content. */
	private LayoutContainer content;
	
	/** The paycheck calculators. */
	private List<PaycheckCalculator> paycheckCalculators;
	
	/** The paycheck calc store. */
	private TreeStore<PaycheckCalculator> paycheckCalcStore;

	/** The property for retrieving the {@link PaycheckCalculatorEditorView} from the {@link Registry}. */
	public static final String PAYCHECK_CALC_EDITOR_VIEW_CONTAINER = "paycheckCalcEditorViewContainer";
	
	/** The property for retrieving the {@link PaycheckCalculatorSummaryView} from the {@link Registry}. */
	public static final String PAYCHECK_SUMMARY_CONTAINER = "paycheckSummaryContainer";
	
	/** The property for retrieving the paycheck calculator store from the {@link Registry}. */
	public static final String PAYCHECK_CALC_STORE = "paycheckCalcStore";
	
	/** The add paycheck button. */
	private Button addPaycheckButton;
	
	/** The paycheck list tree. */
	private TreePanel<PaycheckCalculator> paycheckListTree;
	
	/** The remove paycheck button. */
	private Button removePaycheckButton;

	/**
	 * Instantiates a new paycheck calculator view.
	 *
	 * @param controller the controller
	 */
	public PaycheckCalculatorView(Controller controller) {
		super(controller);

	}

	/* (non-Javadoc)
	 * @see com.extjs.gxt.ui.client.mvc.View#initialize()
	 */
	public void initialize() {

	}

	/**
	 * Handles the <code>Init</code> event.
	 */
	public void initUI() {
		// get the reference to the center panel in the appview
		center = (LayoutContainer) Registry.get(AppView.CENTER_PANEL);
		TabItem paycheckTab = (TabItem) Registry.get(TabView.PAYCHECK_CALC_TAB);

		content = new LayoutContainer(new BorderLayout());
		// create center view to be loaded by EditorView
		LayoutContainer paycheckCalcEditorViewContainer = new LayoutContainer(new FitLayout());
		BorderLayoutData data = new BorderLayoutData(LayoutRegion.CENTER);
		data.setMargins(new Margins(5, 5, 5, 5));
		// This register has to come before the
		Registry.register(PAYCHECK_CALC_EDITOR_VIEW_CONTAINER, paycheckCalcEditorViewContainer);

		content.add(paycheckCalcEditorViewContainer, data);

		data = new BorderLayoutData(LayoutRegion.WEST);
		data.setMargins(new Margins(5, 5, 5, 5));
		content.add(createWest(), data);

		//defaults to not selected
		removePaycheckButton.disable();
		
		// register the paycheck calc store
		Registry.register(PAYCHECK_CALC_STORE, paycheckCalcStore);

		paycheckTab.add(content);
		center.layout();
	}

	/**
	 * Creates the west.
	 *
	 * @return the layout container
	 */
	private LayoutContainer createWest() {

		BorderLayout borderLayout = new BorderLayout();
		LayoutContainer westLayout = new LayoutContainer(borderLayout);

		ContentPanel pcl = buildPaycheckCalculatorList();
		BorderLayoutData data = new BorderLayoutData(LayoutRegion.NORTH);
		data.setMargins(new Margins(0, 0, 5, 0));
		data.setSplit(true);
		westLayout.add(pcl, data);

		LayoutContainer summary = new LayoutContainer();
		data = new BorderLayoutData(LayoutRegion.CENTER);
		data.setSplit(true);
		westLayout.add(summary, data);
		Registry.register(PAYCHECK_SUMMARY_CONTAINER, summary);
		
		fireEvent(AppEvents.PaycheckListLoaded);
		return westLayout;

	}

	/**
	 * Builds the paycheck calculator list.
	 *
	 * @return the content panel
	 */
	private ContentPanel buildPaycheckCalculatorList() {
		ContentPanel paycheckListPanel = new ContentPanel();
		paycheckListPanel.setLayout(new FitLayout());
		// paycheckListPanel.setWidth(250);
		paycheckListPanel.setHeading("Paycheck Calculators");
		paycheckListPanel.setFrame(false);
		ToolBar paycheckListToolBar = new ToolBar();
		addPaycheckButton = new Button("Add");
		addPaycheckButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

			@Override
			public void componentSelected(ButtonEvent ce) {
				handleAddPaycheckButtonClicked();
			}

		});
		removePaycheckButton = new Button("Remove");
		removePaycheckButton.addSelectionListener(new SelectionListener<ButtonEvent>() {

			@Override
			public void componentSelected(ButtonEvent ce) {
				handleRemovePaycheckButtonClicked();
			}

		});
		paycheckListToolBar.add(addPaycheckButton);
		paycheckListToolBar.add(removePaycheckButton);
		paycheckListPanel.setTopComponent(paycheckListToolBar);


		// paycheckCalcStore = new TreeStore<PaycheckCalculator>(loader);
		paycheckCalcStore = new TreeStore<PaycheckCalculator>();
		paycheckCalcStore.setMonitorChanges(true);
		paycheckListTree = new TreePanel<PaycheckCalculator>(paycheckCalcStore);

		paycheckListTree.getStyle().setLeafIcon(IconHelper.createStyle("tree-folder"));
		paycheckListTree.setDisplayProperty("name");
		// tree.setAutoSelect(true);
		paycheckListTree.getSelectionModel().addSelectionChangedListener(
				new SelectionChangedListener<PaycheckCalculator>() {

					@Override
					public void selectionChanged(SelectionChangedEvent<PaycheckCalculator> se) {
						handlePaycheckSelectionChange(se);
					}

					
					
				});
		// pass in false b/c there are no children
		paycheckCalcStore.add(paycheckCalculators, false);
		paycheckCalcStore.addStoreListener(new StoreListener<PaycheckCalculator>(){
			public void storeUpdate(StoreEvent<PaycheckCalculator> se) {
				if (se.getOperation() != null && se.getOperation().equals(Record.RecordUpdate.COMMIT)) {
					
					doPaycheckCalcStoreCommit(se);
				}
			}
		});
		if(paycheckCalcStore.getModels().isEmpty()){
			removePaycheckButton.disable();			
		}
		// TODO select the first one
		/*
		 * if(paycheckCalculators.size() > 0){
		 * tree.getSelectionModel().select(0, 1, false);
		 * 
		 * }
		 */
		paycheckListPanel.add(paycheckListTree);

		return paycheckListPanel;
	}
	
	/**
	 * Do paycheck calc store commit.
	 *
	 * @param se the selection event
	 */
	protected void doPaycheckCalcStoreCommit(StoreEvent<PaycheckCalculator> se) {
		//GWT.log("paycheck commit called for paycheck " + se.getModel().get("name"));
		fireEvent(new AppEvent(AppEvents.PaycheckCalcUpdated, se.getModel()));
		
	}

	/**
	 * Handle paycheck selection change.
	 *
	 * @param se the selection event
	 */
	private void handlePaycheckSelectionChange(SelectionChangedEvent<PaycheckCalculator> se) {
		if (!se.getSelection().isEmpty()) {
			//otherwise changes are remembered across selections.
			paycheckCalcStore.rejectChanges();
			removePaycheckButton.enable();
			PaycheckCalculator pc = (PaycheckCalculator) se.getSelection().get(0);
			//GWT.log("firing paycheck calc selected event!");
			fireEvent(new AppEvent(AppEvents.PaycheckCalcSelected, pc));
		}else{
			/*PaycheckCalculator list selection was cleared. Now we need to notify the editor
			so it clears the form*/
			removePaycheckButton.disable();
			//GWT.log("PaycheckCalculator list selection cleared");
			fireEvent(AppEvents.PaycheckCalcSelectionCleared);							
		}
	}

	/**
	 * Handle remove paycheck button clicked.
	 */
	private void handleRemovePaycheckButtonClicked() {
		final PaycheckCalculator pc = (PaycheckCalculator) paycheckListTree.getSelectionModel().getSelectedItem();
		String msg = "Are you sure you wish to permanently remove " + "the Paycheck Calculator " + pc.getName() + "?";
		MessageBox.confirm("Delete Paycheck Calculator?", msg, new Listener<MessageBoxEvent>() {

			@Override
			public void handleEvent(MessageBoxEvent be) {
				Button button = be.getButtonClicked();

				if (button.getText().equals("Yes")) {

					fireEvent(new AppEvent(AppEvents.DeletePaycheckCalc, pc));
				}

			}
		});
	}

	/**
	 * Handle add paycheck button clicked.
	 */
	protected void handleAddPaycheckButtonClicked() {
		// popup message box to prompt for name
		final Dialog box = new Dialog();

		box.setButtons(MessageBox.OKCANCEL);
		box.setButtonAlign(HorizontalAlignment.CENTER);
		box.setHeading("New Paycheck Calculator");
		// box.addText("Please enter the new Paycheck Calculator name");
		box.setBodyBorder(false);
		// box.setHideOnButtonClick(true);
		box.setModal(true);

		FormPanel formPanel = new FormPanel();
		formPanel.setLabelAlign(LabelAlign.TOP);
		formPanel.setBorders(false);
		formPanel.setHeaderVisible(false);
		formPanel.setBodyBorder(false);
		final TextField<String> nameField = new TextField<String>();
		nameField.setFieldLabel("Please enter the new Paycheck Calculator name");
		nameField.setAllowBlank(false);
		nameField.setMaxLength(16);
		formPanel.add(nameField);
		box.add(formPanel);
		box.getButtonById("ok").addSelectionListener(new SelectionListener<ButtonEvent>() {

			@Override
			public void componentSelected(ButtonEvent ce) {
				if (nameField.isValid()) {				
					addNewPaycheckCalculator(nameField.getValue());
					box.hide();
				} 
			}

		});
		box.getButtonById(Dialog.CANCEL).addSelectionListener(new SelectionListener<ButtonEvent>() {

			@Override
			public void componentSelected(ButtonEvent ce) {
				box.hide();
			}
		});
		box.show();

	}

	/**
	 * Adds the new paycheck calculator.
	 *
	 * @param name the name
	 */
	private void addNewPaycheckCalculator(String name) {
		PaycheckCalculator pc = new PaycheckCalculator();
		pc.setName(name);
		pc.setEstimatedAmt(0);
		pc.setInterval(Interval.Biweekly);
		fireEvent(new AppEvent(AppEvents.NewPaycheckCalc, pc));

	}

	/* (non-Javadoc)
	 * @see com.extjs.gxt.ui.client.mvc.View#handleEvent(com.extjs.gxt.ui.client.mvc.AppEvent)
	 */
	@Override
	protected void handleEvent(AppEvent event) {
		if (event.getType() == AppEvents.Init) {
			//GWT.log("PaycheckCalculatorView handling Init");
			paycheckCalculators = (List<PaycheckCalculator>) event.getData();
			if (!paycheckCalculators.isEmpty()) {
				paycheckCalculators.get(0);
			}
			initUI();
		}
		if (event.getType() == AppEvents.NewPaycheckCalc) {
			//GWT.log("PaycheckCalculatorView handling NewPaycheckCalc");
			// update the model
			PaycheckCalculator pc = (PaycheckCalculator) event.getData();
			paycheckCalcStore.add(pc, false);

			// select the new paycheck
			paycheckListTree.getSelectionModel().select(pc, false);
			dispatchRefreshPayCalcs();
		}
		if (event.getType() == AppEvents.DeletePaycheckCalc) {
			PaycheckCalculator pc = (PaycheckCalculator) event.getData();

			paycheckCalcStore.remove(pc);
			if(!paycheckCalcStore.getAllItems().isEmpty()){
				//GWT.log("selecting first paycheck");
				paycheckListTree.getSelectionModel().select(0, false);
			}
			dispatchRefreshPayCalcs();
		}
		if(event.getType() == AppEvents.CategoryAmountRemoved){
			CategoryAmount ca = (CategoryAmount)event.getData();
			paycheckListTree.getSelectionModel().getSelectedItem().getCategoryAmounts().remove(ca);
		}
		if(event.getType() == AppEvents.CategoryAmountsUpdated){
			List<CategoryAmount> categoryAmts = (List<CategoryAmount>)event.getData();
			PaycheckCalculator p = paycheckListTree.getSelectionModel().getSelectedItem();
			p.setCategoryAmounts(categoryAmts);
			GWT.log("payCalc view firing paycheck calc selected event");
			fireEvent(new AppEvent(AppEvents.PaycheckCalcSelected, p));
		}

	}

	/**
	 * Dispatch <code>RefreshPaycheck</code> event.
	 */
	private void dispatchRefreshPayCalcs() {
		AppEvent e = new AppEvent(AppEvents.RefreshPaycheck);
		e.setData(paycheckCalcStore.getAllItems());
		Dispatcher.get().dispatch(e);
	}

}
